/**
 * @brief 基于时间轮实现定时器
 * @details
 * 1.使用二维数组保存定时器对象的智能指针(shared_ptr),使用unordered_map<uint64_t,weak_ptr>保存定时器id与定时器对象
 * 的weak_ptr的映射关系,在刷新定时器定时器和取消定时器时方便快速找到对应的weak_ptr -> shared_ptr
 * 2.使用channel来保存对象的fd及事件,eventloop来进行事件循环,加入可读事件监控,读到的数字就是超时了多少秒,需要进行n次下标右移
 * 3.在定时器任务(TimerTask)中有一个由时间轮(TimerWheel)设置的回调函数,用于删除TimerWheel中保存的定时器对象信息,即删除对应的weak_ptr
 * 4.TimerTask::_canceled用来表示定时器是否被取消,如果被取消了就不需要执行定时器任务了
 */

#pragma once

#include <functional>
#include <cstdint>
#include <memory>
#include <sys/timerfd.h>
#include "logger.hpp"
#include "channel.hpp"
#include "eventLoop.hpp"

namespace hdp
{
    // 定时器任务
    // 定时器任务的回调函数,即超时时执行的任务
    using TaskFunc = std::function<void()>;
    // 定时器对象销毁的回调函数
    using ReleaseFunc = std::function<void()>;
    class TimerTask
    {
    private:
        uint64_t _id;         // 定时器任务对象id
        uint32_t _timeout;    // 超时定时任务的超时时间
        bool _canceled;       // 超时任务：false表示没有被取消,true表示被取消了
        TaskFunc _task_cb;    // 定时器对象要执行的定时任务
        ReleaseFunc _release; // 用于删除TimerWheel中保存的定时器对象信息
    public:
        TimerTask(const uint64_t &id, const uint32_t &delay, const TaskFunc &cb)
            : _id(id), _timeout(delay), _task_cb(cb), _canceled(false) {}
        ~TimerTask()
        {
            if (_canceled == false)
                _task_cb();
            _release();
        }

        void Canceled() { _canceled = true; }
        uint32_t DelayTime() { return _timeout; }
        void SetRelease(const ReleaseFunc &cb) { _release = cb; }
    };

    class EventLoop;
    // 时间轮
    // 管理定时器任务的shared_ptr
    using PtrTask = std::shared_ptr<TimerTask>;
    // 管理定时器任务的weak_ptr
    using WeakTask = std::weak_ptr<TimerTask>;
    class TimerWheel
    {
    private:
        int _tick;                                      // 当前的秒针,走到哪里释放哪里,就相当于执行哪里的任务
        int _capacity;                                  // 表盘的最大数量,即最大延迟时间
        std::vector<std::vector<PtrTask>> _wheel;       // 报存所有的定时器对象
        std::unordered_map<uint64_t, WeakTask> _timers; // 保存定时器id与对应智能指针的映射关系

        EventLoop *_loop;
        int _timerfd; // 定时器描述符--可读事件回调就是读取定时器,并执行定时任务
        std::unique_ptr<Channel> _timer_channel;

    private:
        /*SetRelease回调函数 -- 从unordered_map中将定时任务信息移除*/
        void RemoveTimer(const uint64_t &id)
        {
            auto it = _timers.find(id);
            if (it != _timers.end())
            {
                _timers.erase(it);
            }
        }

        // 创建定时器描述符
        static int CreateTimerfd()
        {
            // 创建一个定时器
            int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
            if (timerfd < 0)
            {
                LOG_ERROR("create timerfd failed");
                abort();
            }

            // 设置超时时间
            struct itimerspec ims;
            // 第一次超时时间为1s后
            ims.it_value.tv_sec = 1;
            ims.it_value.tv_nsec = 0;
            // 第一次超时后，每次超时的间隔时
            ims.it_interval.tv_sec = 1;
            ims.it_interval.tv_nsec = 0;

            // 启动定时器
            int n = timerfd_settime(timerfd, 0, &ims, nullptr);
            if (n < 0)
            {
                LOG_ERROR("timerfd settime failed");
                abort();
            }
            return timerfd;
        }

        // 读取定时器描述符的数据
        int ReadTimerfd()
        {
            uint64_t times;
            // 有可能因为其他描述符的事件处理花费事件比较长，然后在处理定时器描述符事件的时候，
            // 有可能就已经超时了很多次
            // read读取到的数据times就是从上一次read之后超时的次数
            ssize_t ret = read(_timerfd, &times, 8);
            if (ret < 0)
            {
                LOG_ERROR("read timerfd failed");
                abort();
            }
            return times;
        }

        // 这个函数应该每秒钟被执行一次,相当于秒针向后走了一步
        void RunTimerTask()
        {
            _tick = (_tick + 1) % _capacity;
            // 清空指定位置的数组,就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
            _wheel[_tick].clear();
        }

        // 定时器读事件的回调函数
        void OnTime()
        {
            // 根据实际超时的次数，执行对应的超时任务
            int times = ReadTimerfd();
            for (int i = 0; i < times; i++)
            {
                RunTimerTask();
            }
        }

        // 添加定时任务
        void TimerAddInLoop(const uint64_t &id, const uint32_t &delay, const TaskFunc &cb)
        {
            PtrTask ptr(new TimerTask(id, delay, cb));
            ptr->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
            int pos = (_tick + delay) % _capacity;

            // 将shared_ptr管理的对象加入到时间轮中
            _wheel[pos].push_back(ptr);
            // 将shared_ptr管理的对象的weak_ptr加入到哈希表中
            _timers[id] = WeakTask(ptr);
        }

        // 刷新/延迟定时时间
        void TimerRefreshInLoop(const uint64_t &id)
        {
            // 通过保存的定时器的weak_ptr构造一个shared_ptr出来,添加到轮子中
            auto it = _timers.find(id);
            // 没找着定时任务，没法刷新，没法延迟
            if (it == _timers.end())
            {
                return;
            }
            // lock获取weak_ptr管理的对象对应的shared_ptr
            PtrTask ptr = it->second.lock();
            int delay = ptr->DelayTime();
            int pos = (_tick + delay) % _capacity;
            _wheel[pos].push_back(ptr);
        }

        // 取消定时任务
        void TimerCancelInLoop(const uint64_t &id)
        {
            auto it = _timers.find(id);
            // 没有找到定时任务就没法刷新和延迟,直接退出
            if (it == _timers.end())
                return;
            PtrTask ptr = it->second.lock();
            if (ptr)
                ptr->Canceled();
        }

    public:
        TimerWheel(EventLoop *loop)
            : _tick(0), _capacity(60), _wheel(_capacity),
              _loop(loop), _timerfd(CreateTimerfd()),
              _timer_channel(new Channel(_loop, _timerfd))
        {
            _timer_channel->SetReadCallback(std::bind(&TimerWheel::OnTime, this));
            _timer_channel->EnableRead(); // 启动读事件
        }

        // 定时器有个成员_timers成员,定时器信息的操作有可能在多个线程中进行,因此要考虑线程安全
        // 如果不想加锁,那就把对定时器的所有操作,都放到一个线程中执行
        // 添加定时任务
        /*为了保证添加/刷新/取消定时器任务的线程安全，我们需要在EventLoop对应的线程中去执行它们*/
        // 同时，这里也和Channel::Remove Channel::Update函数一样，我们使用了EventLoop中的成员函数
        // 所以，下面只给出声明，函数的具体实现需要放到EventLoop类下面
        void TimerAdd(const uint64_t &id, const uint32_t &delay, const TaskFunc &cb);
        // 刷新/延迟定时时间
        void TimerRefresh(const uint64_t &id);
        // 取消定时任务
        void TimerCancel(const uint64_t &id);

        // 由于HasTimer的返回值是bool类型，所以无法像Timeradd等函数一样将其放入到EventLoop的任务队列中去执行
        // 存在线程安全问题,不能被外界使用者调用,只能在模块内,在对应的EventLoop线程内调用
        bool HasTimer(const uint64_t &id)
        {
            auto it = _timers.find(id);
            // 没有找到定时任务就没法刷新和延迟,直接退出
            if (it == _timers.end())
                return false;
            return true;
        }
    };
}